const DEFAULT = 'pending';
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';

class MyPromise {
  constructor(executor) {
    try {
      executor(this._resolve, this._reject);
    } catch(e) {
      this._reject(e);
    }
  }
  _status = DEFAULT;   // Promise的状态
  _value = undefined;  // 成功时保存值
  _reason = undefined; // 失败的原因
  _sucCbs = [];        // 链式调用中成功的回调
  _failCbs = [];       // 链式调用中失败的回调

  then = (sucCB = v => v, failCB = reason => { throw reason; }) => { // sucCB和failCB都有默认参数
    const promise2 = new MyPromise((resolve, reject) => { // then 方法返回新的Promise对象
      /**
       * 抽重复代码
       * @param fn 需要调用的方法sucCB或者failCb
       * @param v 需要给回调方法传入的值
       */
      function dealFn(fn, v) {
        queueMicrotask(() => { // 使该方法新增异步函数可以获得promise2对象
          try {  // 捕获异常
            resolvePromise(promise2, fn(v), resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
      if (this._status === SUCCESS) { // 同步如果当前状态成功直接调用成功的回调
        dealFn(sucCB, this._value);
      } else if (this._status === FAIL) { // 同步如果当前状态失败直接调用失败的回调
        dealFn(failCB, this._reason);
      } else { // 等待状态
        this._sucCbs.push(() => dealFn(sucCB, this._value)); // 新增回调方法，等到状态变化的时候再执行这里的方法
        this._failCbs.push(() => dealFn(failCB, this._reason));
      }
    });
    return promise2;
  };

  catch = cb => this.then(undefined, cb);

  /**
   * 通过then方法，再成功回调中调用cb的同时，返回新的promise，用于返回之前传入的值, 在失败回到中也执行cb，并抛出之前的报错信息reason
   * @param cb
   * @returns {MyPromise}
   */
  finally = (cb) => this.then(value => MyPromise.resolve(cb()).then(() => value),
    reason => MyPromise.resolve(cb()).then(() => { throw reason; }));

  static resolve(value) {
    if (value instanceof MyPromise) return value; // 如果使promise类型则直接返回
    return new MyPromise(resolve => resolve(value));
  }

  static all(arr) {
    return new MyPromise((resolve, reject) => {
      const result = new PResults(arr.length);
      arr.forEach((cur, i) => {
        if (cur instanceof MyPromise) { // cur是否为promise对象
          cur.then(value => result.add(value, i, resolve),
              reason => reject(reason));
        } else { // 如果当前不为promise则直接添加到result中
          result.add(cur, i, resolve);
        }
      });
    });
  }

  /**
   * 该Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise，并带有一个对象数组，每个对象表示对应的promise结果。
   * @param arr {MyPromise | any}
   * @returns {MyPromise<{status: 'fulfilled'| 'rejected', value, reason}[]>}
   */
  static allSettled(arr) {
    return new MyPromise((resolve, reject) => {
      const result = new PResults(arr.length);
      try {
        arr.forEach((cur, i) => {
          if (cur instanceof  MyPromise) {
            cur.then(value => result.add({ status: SUCCESS, value }, i, resolve),
              reason => result.add({ status: FAIL, value: reason }, i, resolve))
          } else {
            result.add({status: SUCCESS, value: cur }, i, resolve);
          }
        });
      } catch (e) {
        resolve(e);
      }
    });
  }

  /**
   * 只要其中的一个 promise 成功，就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功（即所有的 promises 都失败/拒绝），就返回一个失败的 promise 和AggregateError类型的实例
   * @param arr
   */
  static any(arr = []) {
    return new MyPromise((resolve, reject) => {
      if (!arr.length) { // 如果传入的参数是一个空的可迭代对象, 这个方法将会同步返回一个已经完成的 promise
        resolve();
        return;
      }
      const pArr = arr.filter(cur => cur instanceof MyPromise); // 找到所有的Promise对象
      const dataArr = arr.filter(cur => !(cur instanceof MyPromise)); // 所有的非Promise对象
      if (dataArr.length) { // 如果存在非Promise对象则直接返回
        resolve(dataArr[0]);
        return;
      }
      let resolved = false;
      const result = new PResults(pArr.length);
      for(const cur of pArr) {
        cur.then((value) => {
          if (!resolved) {  // 如果有一个成功了，则直接返回成功的值，并标记结束
            resolve(value)
            resolved = true;
          }
        }).finally(() => result.add('', 0, () => {
            if (!resolved) { // 如果所有的promise都完成了，并且没有成功的, 则返回AggregateErr
              reject(new AggregateError('No Promise in Promise.any was resolved'));
            }
          })
        )
      }
    })
  }

  /**
   * 方法返回一个 promise，一旦迭代器中的某个promise解决或拒绝，返回的 promise就会解决或拒绝。
   * @param arr
   * @returns {MyPromise}
   */
  static race(arr) {
    return new MyPromise((resolve, reject) => {
      const pArr = arr.filter(cur => cur instanceof MyPromise); // 找到所有的Promise对象
      const dataArr = arr.filter(cur => !(cur instanceof MyPromise)); // 所有的非Promise对象
      if (dataArr.length) {
        resolve(dataArr[0]);
        return;
      }
      let resolved = false;
      pArr.forEach(cur => {
        cur.then(value => {
          if (!resolved) {
            resolved = true;
            resolve(value);
          }
        }, reason => {
          if (!resolved) {
            resolved = true;
            reject(reason);
          }
        })
      });
    })
  }
  static reject(reason) {
    return new MyPromise((r, reject) => { reject(reason); });
  }
  /**
   * 如果当前为默认状态，则修改状态为fulfilled并保存值
   * @param value
   * @private
   */
  _resolve = (value) => {
    if (this._status !== DEFAULT) return; // 状态已经改变后直接return掉
    this._value = value; // 设置值
    this._status = SUCCESS; // 修改状态为成功
    while(this._sucCbs.length) {
      this._sucCbs.shift()();
    }
  };

  /**
   * 如果当前为默认状态，则修改状态为rejected并保存错误原因
   * @param reason
   * @private
   */
  _reject = (reason) => {
    if (this._status !== DEFAULT) return;
    this._reason = reason;
    this._status = FAIL;
    while(this._failCbs.length) {
      this._failCbs.shift()();
    }
  };

}

class AggregateError extends Error {}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}

class PResults {
  constructor(length) {
    this._length = length;
  }
  _count = 0;
  _result = [];
  add = (v, idx, cb) => {
    this._result[idx] = v;
    this._count ++;
    if (this._count === this._length) {
      cb(this._result);
    }
  }
}

module.exports = MyPromise;
